package tools

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/gob"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"math"
	mrand "math/rand"
	"net/url"
	"reflect"
	"server_cluster/http/http_config"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unsafe"

	"server_cluster/common/config"
)

var loc *time.Location
var rand_seed uint32

func MySeed(seed int32) {
	rand_seed = uint32(seed)
	mrand.Seed(int64(seed))
}

func MathRand(min int, max int) int {
	return mrand.Intn(max-min+1) + min
}

func MyRand(min int32, max int32) int32 {
	if max <= min {
		return min
	}
	rand_seed = (rand_seed*163 + 59)
	interval := uint32(max - min + 1)
	r := (rand_seed/(100000000/interval))%interval + uint32(min)
	log.Printf("########## MyRand rand=%d seed=%d min=%d max=%d", r, rand_seed, min, max)
	return int32(r)
}

func init() {
	var err error
	loc, err = time.LoadLocation("Asia/Shanghai")
	if err != nil {
		loc = time.FixedZone("Beijing", 8*3600)
	}
	MySeed(int32(TimeNow()))
}

func TimeNow() int64 {
	return time.Now().In(loc).Unix()
}

func TimeNowObj() time.Time {
	return time.Now().In(loc)
}

func ParseTimeToObj(ts int64) time.Time {
	return time.Unix(ts, 0)
}

func TimeNowStr() time.Time {
	return time.Now().In(loc)
}

func Time2timestamp(mytime time.Time) int64 {
	return mytime.Unix()
}

func Str2timestamp(str string) int64 {
	layout := "2006-01-02-15:04:05"
	t, err := time.ParseInLocation(layout, str, loc)

	if err != nil {
		return -1
	}

	return t.Unix()
}

func Timestamp2time(mytime int64) time.Time {
	return time.Unix(mytime, 0).In(loc)
}

func GetCurDayZeroTime() int64 {
	var nowTime = TimeNowObj()
	nowTime.Truncate(time.Hour * 24)
	return nowTime.Unix()
}
func GetNextDayZeroTime() int64 {
	var nowTime = TimeNowObj()
	nowTime.Truncate(time.Hour * 24)
	nowTime.AddDate(0, 0, 1)
	return nowTime.Unix()
}

func EnSession(index int32, uid int64, created int64) uint64 {
	a := (uid%10000 + created%10000) % 10000
	b := int64(index)*10000 + a
	return uint64(b ^ config.RANDOM)
}

func DeSession(session uint64) uint32 {
	return uint32((session ^ config.RANDOM) / 10000)
}

func CreateAesKey(fix int, ext bool) []byte {
	key := []byte(config.KEY)
	if !ext {
		key = []byte(config.HASH_KEY)
	}
	offset := len(key) / 2
	if offset != 8 && offset != 12 && offset != 16 {
		panic(fmt.Sprintf("key 长度必须是 16/24/32"))
	}

	b_fix := []byte(strconv.Itoa(fix))
	len_fix := len(b_fix)
	for i := 0; i < offset; i++ {
		if i < len_fix {
			key[i+offset] = b_fix[i]
		} else {
			break
		}
	}
	return key
}

func PKCS7Padding(data []byte, size int) []byte {
	padding := size - len(data)%size
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(data, padtext...)
}

func PKCS7UnPadding(data []byte) []byte {
	length := len(data)
	unpadding := int(data[length-1])
	if length < unpadding {
		return data[0:0]
	}
	return data[:(length - unpadding)]
}

func AesEncrypt(data []byte, fix int, ext bool) []byte {
	k := CreateAesKey(fix, ext)
	block, _ := aes.NewCipher(k)
	blockSize := block.BlockSize()
	data = PKCS7Padding(data, blockSize)
	blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
	cryted := make([]byte, len(data))
	blockMode.CryptBlocks(cryted, data)
	return cryted
}

func AesDecrypt(data []byte, fix int, ext bool) []byte {
	k := CreateAesKey(fix, ext)
	block, _ := aes.NewCipher(k)
	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
	length := len(data)
	decryted := make([]byte, length)
	if length%blockSize == 0 {
		blockMode.CryptBlocks(decryted, data)
		decryted = PKCS7UnPadding(decryted)
	}
	return decryted
}

func CreateUserFightSign(uid int64, fids []int32, timestamp int64) string {
	res := fmt.Sprintf("%d%d", uid, timestamp^185472)
	for _, v := range fids {
		res += fmt.Sprintf("%d", v)
	}
	return HashEncrypt(res)
}

func HashEncrypt(data string) string {
	m := md5.New()
	io.WriteString(m, data+config.HASH_KEY)
	return fmt.Sprintf("%x", m.Sum(nil))
}

func DeepMarshal(src any) ([]byte, error) {
	var buf bytes.Buffer
	err := gob.NewEncoder(&buf).Encode(src)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func DeepUnmarshal(dst any, data []byte) error {
	err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(dst)
	if err != nil {
		return err
	}
	return nil
}

func GetStructFieldValue(structPointer any, fieldName string) (bool, any) {
	refType := reflect.TypeOf(structPointer)
	for i := 0; i < 100; i++ {
		if refType.Kind() != reflect.Ptr {
			break
		}
		refType = refType.Elem()
	}
	if refType.Kind() != reflect.Struct {
		return false, nil
	}
	refValue := reflect.ValueOf(structPointer)
	for i := 0; i < 100; i++ {
		if refValue.Kind() != reflect.Ptr {
			break
		}
		refValue = refValue.Elem()
	}
	field := refValue.FieldByName(fieldName)
	return true, field.Interface()
}

func SetStructFieldValue(structPointer any, fieldName string, value any) bool {
	refType := reflect.TypeOf(structPointer)
	for i := 0; i < 100; i++ {
		if refType.Kind() != reflect.Ptr {
			break
		}
		refType = refType.Elem()
	}
	if refType.Kind() != reflect.Struct {
		return false
	}
	refValue := reflect.ValueOf(structPointer)
	for i := 0; i < 100; i++ {
		if refValue.Kind() != reflect.Ptr {
			break
		}
		refValue = refValue.Elem()
	}
	field := refValue.FieldByName(fieldName)
	if field.Type() != reflect.TypeOf(value) {
		return false
	}
	field.Set(reflect.ValueOf(value))
	return true
}

func GetArrayFieldValue(arrayPointer any, index int) (bool, any) {
	refType := reflect.TypeOf(arrayPointer)
	for i := 0; i < 100; i++ {
		if refType.Kind() != reflect.Ptr {
			break
		}
		refType = refType.Elem()
	}
	if refType.Kind() != reflect.Array {
		return false, nil
	}
	refValue := reflect.ValueOf(arrayPointer)
	for i := 0; i < 100; i++ {
		if refValue.Kind() != reflect.Ptr {
			break
		}
		refValue = refValue.Elem()
	}
	field := refValue.Index(index)
	return true, field.Interface()
}

func SetArrayFieldValue(arrayPointer any, index int, value any) bool {
	refType := reflect.TypeOf(arrayPointer)
	for i := 0; i < 100; i++ {
		if refType.Kind() != reflect.Ptr {
			break
		}
		refType = refType.Elem()
	}
	if refType.Kind() != reflect.Array {
		return false
	}
	refValue := reflect.ValueOf(arrayPointer)
	for i := 0; i < 100; i++ {
		if refValue.Kind() != reflect.Ptr {
			break
		}
		refValue = refValue.Elem()
	}
	field := refValue.Index(index)
	if field.Type() != reflect.TypeOf(value) {
		return false
	}
	field.Set(reflect.ValueOf(value))
	return true
}

func IntArray2string(ids []int) string {
	var key string
	for _, v := range ids {
		key += strconv.Itoa(v)
	}
	return key
}

// data 原始数据 num 随机抽取次数
func GetDataByWeight(data map[int32]int32, num int) []int32 {
	data_len := len(data)
	if data_len <= 0 {
		return nil
	}
	total := 0
	for _, v := range data {
		total += int(v)
	}

	ret := make([]int32, 0)
	for i := 0; i < num; {
		w := MyRand(0, int32(total))
		for k, v := range data {
			if w < v {
				ret = append(ret, k)
				i++
				break
			}
			w -= v
		}
	}
	return ret
}

func GetResultByPro(pro int32) bool {
	if MyRand(0, 10000) < pro {
		return true
	}
	return false
}

func GetResultByRegion(min int, max int) int32 {
	return MyRand(int32(min), int32(max))
}

func Remove(slice []int32, index int) []int32 {
	copy(slice[index:], slice[index+1:])
	return slice[:len(slice)-1]
}

func String2Array(str string, sep string) []int32 {
	res := make([]int32, 0)
	if len(str) <= 0 {
		return res
	}

	list := strings.Split(str, sep)
	for _, v := range list {
		arg, err := strconv.Atoi(v)
		if err != nil {
			continue
		}
		res = append(res, int32(arg))
	}
	return res
}

func InterfaceToStruct(i interface{}, target interface{}) error {
	val := reflect.ValueOf(target)
	if val.Kind() != reflect.Ptr || val.IsNil() {
		return fmt.Errorf("target must be a non-nil pointer")
	}

	rv := reflect.ValueOf(i)
	if rv.Type() != val.Elem().Type() {
		return fmt.Errorf("type mismatch: %v != %v", rv.Type(), val.Elem().Type())
	}

	val.Elem().Set(rv)
	return nil
}

func SubAtLeastZero(n1 int64, n2 int64) int64 {
	return int64(math.Max(0, float64(n1-n2)))
}

func String2Bytes(s string) []byte {
	return unsafe.Slice(unsafe.StringData(s), len(s))
}

func Bytes2String(b []byte) string {
	return unsafe.String(&b[0], len(b))
}

func QueryMd5(data *url.Values) string {
	if data == nil {
		return ""
	}
	var keySlice = make([]string, 0)
	for key, _ := range *data {
		keySlice = append(keySlice, key)
	}
	sort.Strings(keySlice)
	var paramStr = ""
	for _, v := range keySlice {
		value := data.Get(v)
		if value == "" {
			return ""
		}
		paramStr += fmt.Sprintf("%v", data)
	}
	paramStr += http_config.GetConfig().BiliSecret
	sign := md5.Sum(String2Bytes(paramStr))
	return Bytes2String(sign[:])
}

func CheckString(input string) bool {
	for _, r := range input {
		if !(unicode.IsLetter(r) && unicode.IsNumber(r)) {
			return false
		}
	}

	return true
}

func GenerateRandomString(length int) (string, error) {
	// 计算需要的字节数
	// base64编码会将3字节转换为4字符，因此需要调整输入字节数
	// 向上取整：bytesNeeded = ceil(length * 3 / 4)
	bytesNeeded := (length*3 + 3) / 4

	// 生成随机字节
	randomBytes := make([]byte, bytesNeeded)
	_, err := rand.Read(randomBytes)
	if err != nil {
		return "", err
	}

	// 编码为base64
	encoded := base64.RawURLEncoding.EncodeToString(randomBytes)

	// 截取到指定长度
	if len(encoded) > length {
		return encoded[:length], nil
	}

	return encoded, nil
}

func BuildPassword(password, key string) string {
	md5hash := md5.New()
	_, err := md5hash.Write([]byte(password + key))
	if err != nil {
		return ""
	}
	return hex.EncodeToString(md5hash.Sum(nil))
}

func GetMidnight(timestamp int64) time.Time {
	t := time.Unix(timestamp, 0)
	return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}

func GetMidnightSec(timestamp int64) int64 {
	t := GetMidnight(timestamp)
	return t.Unix()
}
